import time
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import Select

from domains.QuestionParser import QuestionParser
from utils.lg import logger
from domains.ConfigParser import ConfigParser
from domains.Question import QueTypes
from settings import IS_HEADLESS


class Assistant:
    def __init__(self, driver: webdriver.Chrome, config_parser: ConfigParser,
                 que_parser: QuestionParser | list[QuestionParser]):
        self.driver = driver
        self.configs = config_parser.configs
        if isinstance(que_parser, QuestionParser):
            self.parsers = [que_parser]
        else:
            self.parsers = que_parser
        self.parser = self.parsers[0]
        self.reports = []

    @logger.catch
    def run(self, times=1):
        # 登录lry网站
        self._connect_url()
        # 是否最大化
        if self.configs["is_max_size"]:
            self.driver.maximize_window()
        login_warning = self._login_lry()
        if login_warning:
            # print(login_warning)
            logger.warning(login_warning)
            return
        # 进行自动化答题
        logger.info("开始自动答题")
        for i in range(times):
            for parser in self.parsers:
                # 获取当前的解析对象，进行对应试题的回答
                self.parser = parser
                logger.info(f"当前轮数: {i + 1} | 问题页面: {self.parser.problem_href}")
                # 前往对应试题网页
                self._connect_url()
                # 进行作答
                self._answering()
                logger.info("当前问题界面回答完毕")
                # 记录本次作答的结果报告
                self._generate_result_report()
                logger.info(f"生成报告: {self.reports[-1]}")
                time.sleep(self.configs["finished_delay"])
        logger.info("完成自动答题")

    def _connect_url(self):
        # 去到self.parser.problem_href
        logger.info(f"连接: {self.parser.problem_href}")
        self.driver.get(self.parser.problem_href)
        time.sleep(self.configs["loading_delay"])

    def _login_lry(self):
        # 选择用户登录
        logger.info("点击用户登录按钮")
        self.driver.find_element(By.CSS_SELECTOR, '#ssobtn').click()
        time.sleep(self.configs["loading_delay"])

        # 输入账号密码
        logger.info("输入账号")
        self.driver.find_element(By.CSS_SELECTOR, "#account").send_keys(self.configs["user_name"])
        logger.info("输入密码")
        self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(self.configs["user_psw"])
        logger.info("点击登录")
        self.driver.find_element(By.CSS_SELECTOR, "#btn-password-login").click()
        time.sleep(self.configs["loading_delay"])

        # 判断是否账号或密码错误
        login_msg = ""
        try:
            login_msg = self.driver.find_element(By.CSS_SELECTOR, ".modal-content .modal-body #msgtext").text
        except NoSuchElementException:
            pass
        if login_msg:
            # 用户账号不得包含除大小写字母和数字以外的字符，请检查是否输入了空格、占位符、换行符或其他的特殊字符
            return "账号或密码错误: " + login_msg

        # 点击确认登录lry
        logger.info("点击确认登录按钮")
        self.driver.find_element(By.CSS_SELECTOR, ".float-right.login-check-comfirm a").click()
        time.sleep(self.configs["loading_delay"])
        return ""

    def _answering(self):
        logger.info("点击开始答题/重新作答测验按钮")
        self.driver.find_element(By.CSS_SELECTOR, ".btn.btn-secondary").click()
        time.sleep(self.configs["loading_delay"])
        logger.info("点击确认按钮")
        self.driver.find_element(By.CSS_SELECTOR, "input[type='submit']").click()
        time.sleep(self.configs["loading_delay"])
        # 之前以为是一个页面把题目给完，后来发现是一题一页，遂重写
        # for que_box in que_boxs:
        #     desc = que_box.find_element(By.CSS_SELECTOR, ".qtext p[dir='ltr']").text.split('\n[:[0]DESC_LEN]
        #     self._answer(que_box, self.parser.desc2que[desc])
        #     time.sleep(self.configs["answering_delay)
        for i in range(self.parser.que_count):
            # 按提供的答案的题目个数来迭代
            # 找到题目区域
            logger.info(f"正在回答问题 {i + 1}")
            logger.debug("找到问题区域")
            que_box = self.driver.find_element(By.CSS_SELECTOR, ".formulation.clearfix")
            # 获取题目类型
            que_type = self.driver.find_element(By.CSS_SELECTOR, "[id*='question']").get_attribute("class").split()[1]
            logger.debug(f"获得问题类型: {que_type}")
            # 根据题目类型进行不同的处理
            self._answer(que_box, que_type)
            # 点击下一页
            logger.info("结束答题并点击下一页按钮")
            self.driver.find_element(By.CSS_SELECTOR, "input[name='next']").click()
            time.sleep(self.configs["answering_delay"])
        # 结束页面有多个按钮，直接找文本对应的
        btn_lis = self.driver.find_elements(By.CSS_SELECTOR, "button.btn.btn-secondary")
        # 结束
        for btn in btn_lis:
            if btn.text == "全部提交并结束":
                logger.info("点击提交按钮")
                btn.click()
                time.sleep(self.configs["loading_delay"])
                # 确认
                logger.info("点击确认按钮")
                self.driver.find_element(By.CSS_SELECTOR, "input[type='button'][value='全部提交并结束']").click()

    def _answer(self, que_box: WebElement, que_type: str):
        if que_type == QueTypes.GAPS:
            self._answer_gaps(que_box)
        elif que_type == QueTypes.MUL_ANS:
            self._answer_mul_ans(que_box)
        elif que_type == QueTypes.CHOSE:
            self._answer_chose(que_box)
        elif que_type == QueTypes.SHORT_ANS:
            self._answer_short_ans(que_box)
        elif que_type == QueTypes.JUDGE:
            self._answer_judge(que_box)
        elif que_type == QueTypes.CHOSE_ETC:
            self._answer_chose_etc(que_box)
        elif que_type == QueTypes.ESSAY:
            self._answer_essay(que_box)

    def _answer_essay(self, que_box: WebElement):
        # 找到题目区域并提取题目描述
        desc = que_box.find_element(By.CSS_SELECTOR, ".qtext > p  > span").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 找到答题区并填入答案
        logger.debug("填入答案")
        text_box = que_box.find_element(By.CSS_SELECTOR, "[role='textbox']")
        text_box.send_keys(que.ans_list[0])

    def _answer_gaps(self, que_box: WebElement):
        # 找到题目区域并提取题目描述
        desc = que_box.find_element(By.CSS_SELECTOR, ".qtext p").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 找到答题区并填入答案
        logger.debug("填入答案")
        for slt_tag, ans in zip(que_box.find_elements(By.CSS_SELECTOR, "select.select"), que.ans_list):
            # 多少个下拉框就有多少个ans，使用zip
            slt = Select(slt_tag)
            slt.select_by_visible_text(ans)

    def _answer_mul_ans(self, que_box: WebElement):
        # 找到题目区域并提取题目描述
        # 有的题目标签的地方会不一样，需要try一下
        try:
            desc = que_box.find_element(By.CSS_SELECTOR, ".qtext p").text.split('\n')[0].replace('\xa0', ' ')
        except NoSuchElementException:
            desc = que_box.find_element(By.CSS_SELECTOR, "p[dir='ltr']").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 找到答题区并填入答案
        logger.debug("填入答案")
        spans = que_box.find_elements(By.CSS_SELECTOR, "p span.subquestion.form-inline.d-inline")
        for span, ans in zip(spans, que.ans_list):
            # 多少个填空位置就有多少个ans，使用zip
            span.find_element(By.CSS_SELECTOR, "input").send_keys(ans)

    def _answer_chose(self, que_box: WebElement):
        # 找到题目
        # 多选题会比单选多个p标签(题目和选项都是)
        more = ""
        try:
            desc = que_box.find_element(By.CSS_SELECTOR, ".qtext p").text.split('\n')[0].replace('\xa0', ' ')
            more = " p"
        except NoSuchElementException:
            desc = que_box.find_element(By.CSS_SELECTOR, ".qtext").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 找到答题区并填入答案
        logger.debug("填入答案")
        choose_elements = que_box.find_elements(By.CSS_SELECTOR, ".answer>div")
        # 不一定所有选项都要选，不能使用zip
        for choose_element in choose_elements:
            # 每个选项的框
            if choose_element.find_element(By.CSS_SELECTOR, ".flex-fill.ml-1" + more).text in que.ans_list:
                # 如果文本内容在ans_list中，点击该框内的input按钮
                # 有的是圆的选择框，有的是方的选择框，很无语
                try:
                    choose_element.find_element(By.CSS_SELECTOR, "input[type='checkbox']").click()
                except NoSuchElementException:
                    choose_element.find_element(By.CSS_SELECTOR, "input[type='radio']").click()

    def _answer_short_ans(self, que_box: WebElement):
        # 找到题目
        desc = que_box.find_element(By.CSS_SELECTOR, ".qtext").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 简答题直接填入答案
        logger.debug("填入答案")
        que_box.find_element(By.CSS_SELECTOR, ".answer input").send_keys(que.ans_list[0])

    def _answer_judge(self, que_box: WebElement):
        # 找到题目
        desc = que_box.find_element(By.CSS_SELECTOR, ".qtext p").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 和_answer_chose类似，先保留每个选项的标签框，再一个个判断是否是要选的答案，然后再寻找框内的input按钮点击
        # 这里choose_elements理论上就只有两个
        logger.debug("填入答案")
        choose_elements = que_box.find_elements(By.CSS_SELECTOR, ".answer div")
        for choose_element in choose_elements:
            # 判断题选项的文本在label标签中
            if choose_element.find_element(By.CSS_SELECTOR, "label").text in que.ans_list:
                choose_element.find_element(By.CSS_SELECTOR, "input").click()

    def _answer_chose_etc(self, que_box: WebElement):
        # 找到题目
        desc = que_box.find_element(By.CSS_SELECTOR, ".qtext>p").text.split('\n')[0].replace('\xa0', ' ')
        logger.debug(f"获取到问题描述: {desc}")
        # 找到答案
        que = self.parser.desc2que[desc]
        logger.info(f"获取到答案: {que.ans_list}")
        # 找到答题区并填入答案
        logger.debug("填入答案")
        choose_elements = que_box.find_elements(By.CSS_SELECTOR, ".answer>div")
        for choose_element in choose_elements:
            # multichoiceset 相比 multichoice (_answer_chose方法)多个p标签
            if choose_element.find_element(By.CSS_SELECTOR, ".flex-fill.ml-1 p").text in que.ans_list:
                # 除了 input[type="checkbox"] 还有 input[type="hidden"] (后面这个点不到)
                choose_element.find_element(By.CSS_SELECTOR, "input[type='checkbox']").click()

    def _generate_result_report(self):
        # 6栏报告
        trs = self.driver.find_elements(By.CSS_SELECTOR, ".generalbox td")
        cost = trs[3].text
        correct = trs[4].text
        if correct == "还未评分":
            score = "还未评分"
        else:
            score = trs[5].text
        self.reports.append(f"用时: {cost}  正确率: {correct}  分数: {score}")


if __name__ == '__main__':
    options = Options()
    # 去掉自动化测试的提示
    options.add_experimental_option('useAutomationExtension', False)
    options.add_experimental_option("excludeSwitches", ['enable-automation'])
    # 取消保存密码提示框
    prefs = {"credentials_enable_service": False,
             "profile.password_manager_enabled": False
             }
    options.add_experimental_option("prefs", prefs)
    # 设置无头模式
    if IS_HEADLESS:
        options.add_argument('--headless')
    # 获取driver对象
    driver = webdriver.Chrome(options)

    # 获取解析器对象
    qp = QuestionParser(r'D:\Code\Python_code\LryAssistant\temp\第3章数据表示--视频自学测试1：答题回顾.html')

    # 获取配置文件解析器
    cp = ConfigParser(r"D:\Code\Python_code\LryAssistant\configs.json")

    # 获取Assistant对象
    # print(qp.desc2que.keys())
    ass = Assistant(driver, cp, qp)

    # 执行工作方法
    ass.run(times=1)
